package com.pro.common.modules.service.dependencies.util;

import cn.hutool.core.lang.Snowflake;
import com.pro.common.api.exception.BusinessException;
import com.pro.common.api.enums.IEnumOrderEntityType;
import com.pro.framework.api.util.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.function.Supplier;

@Slf4j
@Component
public class TransactionUtil {
    private static ThreadPoolTaskExecutor executor;
    private static Snowflake snowflake;

    @Autowired
    public void setExecutor(ThreadPoolTaskExecutor executor) {
        TransactionUtil.executor = executor;
    }

    @Autowired
    public void setSnowflake(Snowflake snowflake) {
        TransactionUtil.snowflake = snowflake;
    }

    /**
     * 事务后执行
     */
    public static void doAfter(Runnable runnable) {
        // 如果有事务 事务后执行
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    executor.execute(runnable);
                }
            });
        }
        // 如果无事务 直接执行
        else {
            runnable.run();
        }
    }


    /**
     * 重试
     */
    public static <T> T retry(int maxAttempts, long waitMillis, Supplier<T> task, String errorMsg, Object... params) {
        for (int i = 1; i <= maxAttempts; i++) {
            try {
                return task.get();
            } catch (Exception e) {
                log.warn("{}，已重试 {} 次仍失败 {}", errorMsg, i - 1, ExceptionUtil.logThrowable(e));
                if (i == maxAttempts) {
                    throw new BusinessException(errorMsg, params);
                }
                try {
                    Thread.sleep(waitMillis);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt(); // 保留中断状态
                    log.warn("{} \n{}", errorMsg, ExceptionUtil.logThrowable(e));
                    throw new BusinessException(errorMsg, params);
                }
            }
        }
        return null;
    }

    public static String generateOrderNo(IEnumOrderEntityType entity) {
        return entity.getShortName() + snowflake.nextId();
    }


    /**
     * 针对网络请求的重试（只对超时异常进行重试）
     */
    public static <T> T retryOnTimeout(int maxAttempts, long waitMillis, Supplier<T> task, String msg, Object... params) {
        return retryOnTimeout(maxAttempts, waitMillis, task, msg, false, params);
    }

    /**
     * 针对网络请求的重试（只对超时异常进行重试）
     */
    public static <T> T retryOnTimeout(int maxAttempts, long waitMillis, Supplier<T> task, String msg, boolean logDetail, Object... params) {
        for (int i = 1; i <= maxAttempts; i++) {
            try {
                return task.get();
            } catch (Exception e) {
                // 只对超时异常进行重试
                if (ExceptionUtil.isRetryableException(e)) {
                    log.warn("{}，第 {} 次尝试超时 \n{}", msg, i, logDetail ? ExceptionUtil.logThrowable(e) : e.getMessage());
                    if (i == maxAttempts) {
                        throw new BusinessException(msg, params);
                    }
                    sleep(waitMillis);
                } else {
                    // 非超时异常直接抛出，不重试
                    throw e;
                }
            }
        }
        return null;
    }
//    public static boolean isRetryableException(Throwable e) {
//        return e instanceof java.net.SocketTimeoutException
//                || e instanceof java.net.UnknownHostException
//                || (e.getCause() != null && isRetryableException(e.getCause()));
//    }

//    private static boolean isTimeoutException(Throwable e) {
//        return e instanceof java.net.SocketTimeoutException
//                || (e.getCause() != null && isTimeoutException(e.getCause()));
//    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("重试等待被中断", ie);
        }
    }
}
